/* This source file is part of the ATMEL AVR-UC3-SoftwareFramework-1.7.0 Release */

/*This file is prepared for Doxygen automatic documentation generation.*/
/*! \file ******************************************************************
 *
 * \brief Management of the USB device CDC task.
 *
 * This file manages the USB device CDC task.
 *
 * - Compiler:           IAR EWAVR32 and GNU GCC for AVR32
 * - Supported devices:  All AVR32 devices with a USB module can be used.
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 ***************************************************************************/

/* Copyright (c) 2009 Atmel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an Atmel
 * AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
 *
 */

//_____  I N C L U D E S ___________________________________________________

#include <stdio.h>
#include "usart.h"     // Shall be included before FreeRTOS header files, since 'inline' is defined to ''; leading to
                       // link errors
#include "conf_usb.h"


#if USB_DEVICE_FEATURE == ENABLED

#include "board.h"
#include "FreeRTOS.h"
#include "task.h"
#include "usb_drv.h"
#include "gpio.h"
#include "usb_descriptors.h"
#include "usb_standard_request.h"
#include "device_cdc_task.h"
#include "uart_usb_lib.h"
#include "string.h"


//_____ M A C R O S ________________________________________________________


//_____ D E F I N I T I O N S ______________________________________________

//_____ D E C L A R A T I O N S ____________________________________________

static volatile U16  sof_cnt;



//!
//! @brief This function initializes the hardware/software resources
//! required for device CDC task.
//!
void device_cdc_task_init(void)
{
  sof_cnt   =0 ;
  uart_usb_init();

#ifndef FREERTOS_USED
  #if USB_HOST_FEATURE == ENABLED
  // If both device and host features are enabled, check if device mode is engaged
  // (accessing the USB registers of a non-engaged mode, even with load operations,
  // may corrupt USB FIFO data).
  if (Is_usb_device())
  #endif  // USB_HOST_FEATURE == ENABLED
    Usb_enable_sof_interrupt();
#endif  // FREERTOS_USED

#ifdef FREERTOS_USED
  xTaskCreate(device_cdc_task,
              configTSK_USB_DCDC_NAME,
              configTSK_USB_DCDC_STACK_SIZE,
              NULL,
              configTSK_USB_DCDC_PRIORITY,
              NULL);

#endif  // FREERTOS_USED
}

#define USB_BUFFER_SIZE 1024 * 4
#define USB_BUFFER_AFULL 10

unsigned char usb_in_buffer[USB_BUFFER_SIZE];
unsigned char usb_out_buffer[USB_BUFFER_SIZE];

unsigned int  usb_in_buffer_index = 0,usb_read_index = 0;
unsigned int  usb_out_buffer_index = 0,usb_write_index = 0;

unsigned int flag_afull = 0;

void usb_print_char(char letter)
{
	if( uart_usb_tx_ready() )      // "USART"-USB free ?
	{
		uart_usb_putchar(letter);
	}
}

void usb_output(void)
{
	int i,current_index = usb_write_index;
	if(current_index >= usb_out_buffer_index)
	{
		for(i = usb_out_buffer_index ; i < current_index ; i++)
			usb_print_char(usb_out_buffer[i]);
	}
	else
	{
		for(i = usb_out_buffer_index ; i < USB_BUFFER_SIZE ; i++)
			usb_print_char(usb_out_buffer[i]);
		for(i = 0 ; i < current_index ; i++)
			usb_print_char(usb_out_buffer[i]);
	}
	usb_out_buffer_index = current_index;
}

int usb_count(void)
{
	int count = usb_in_buffer_index - usb_read_index;
	if (count < 0)
		count += USB_BUFFER_SIZE;
	return count;
}

unsigned int usb_get_data(unsigned char *data,unsigned int size)
{

	int l_size = usb_count();
	if(!l_size)
	{
		if(flag_afull)
		{
			flag_afull = 0;
		}
		return 0;
	}
	if (size > l_size)
		size = l_size;

	l_size = size;

	if (usb_read_index + l_size >= USB_BUFFER_SIZE)
	{
		int count;
		count = USB_BUFFER_SIZE - usb_read_index;
		memcpy(data,&usb_in_buffer[usb_read_index],count);
		l_size -= count;
		usb_read_index = 0;
		memcpy(&data[count],usb_in_buffer,l_size);
	}
	else
		memcpy(data,&usb_in_buffer[usb_read_index],l_size);
	if(flag_afull)
	{
		if((USB_BUFFER_SIZE - usb_count()) > USB_BUFFER_AFULL)
		{
			flag_afull = 0;
		}
	}
	usb_read_index += l_size;
	return size;
}

void usb_put_data(unsigned char * data,unsigned int size)
{
	unsigned int l_size = size;
	if (usb_write_index + l_size >= USB_BUFFER_SIZE)
	{
		int count;
		count = USB_BUFFER_SIZE - usb_write_index;
		memcpy(&usb_out_buffer[usb_write_index],data,count);
		l_size -= count;
		usb_write_index = 0;
		memcpy(usb_out_buffer,&data[count],l_size);
	}
	else
		memcpy(&usb_out_buffer[usb_write_index],data,l_size);

	usb_write_index += l_size;
}


#ifdef FREERTOS_USED
void device_cdc_task(void *pvParameters)
#else
void device_cdc_task(void)
#endif
{
	int c;
	static Bool startup=TRUE;

	portTickType xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();

	while (TRUE)
	{
		//vTaskDelayUntil(&xLastWakeTime, configTSK_USB_DCDC_PERIOD);

		// First, check the device enumeration state
		if (!Is_device_enumerated())
			continue;
		if( startup )
			startup=FALSE;

		if( sof_cnt>=NB_MS_BEFORE_FLUSH )  //Flush buffer in Timeout
		{
			sof_cnt=0;
			uart_usb_flush();
		}
		if (uart_usb_test_hit())          // Something received from the USB ?
		{
			if(flag_afull)
				continue;
			c= uart_usb_getchar();
			usb_in_buffer[usb_in_buffer_index] = c;
			usb_in_buffer_index++;
			if(usb_in_buffer_index == USB_BUFFER_SIZE)
				usb_in_buffer_index = 0;
			if(usb_read_index > usb_in_buffer_index)
			{
				if((usb_read_index - usb_in_buffer_index) < USB_BUFFER_AFULL)
				{
					flag_afull = 1;
				}
			}
			else
			{
				if(usb_read_index < USB_BUFFER_AFULL)
				{
					if((USB_BUFFER_SIZE - usb_in_buffer_index + usb_read_index) < USB_BUFFER_AFULL)
					{
						flag_afull = 1;
					}
				}
			}
		}
		usb_output();
/*		if (uart_usb_test_hit())          // Something received from the USB ?
		{
			c= uart_usb_getchar();
			if( uart_usb_tx_ready() )      // "USART"-USB free ?
			{
				uart_usb_putchar(c + 1);
			}
		}*/
	}
}



//!
//! @brief usb_sof_action
//!
//! This function increments the sof_cnt counter each time
//! the USB Start-of-Frame interrupt subroutine is executed (1 ms).
//! Useful to manage time delays
//!
void usb_sof_action(void)
{
  sof_cnt++;
}

#endif  // USB_DEVICE_FEATURE == ENABLED
